home *** CD-ROM | disk | FTP | other *** search
/ Mesolore / Mesolore - Disc 1.iso / pc / data / Buttons.cst / 00185_Script_Custom Scrollbar < prev    next >
Text File  |  2001-04-13  |  36KB  |  1,039 lines

  1. -- DESCRIPTION --
  2.  
  3. on getBehaviorDescription me
  4.   return "¼
  5. CUSTOM SCROLL BAR"&RETURN&RETURN&"¼
  6. Create dynamic scrollbars with your own artwork.  Such scrollbars are not ¼
  7. Direct-To-Stage, so other sprites can appear over the top of them."&¼
  8. RETURN&RETURN&"¼
  9. You will need to create four graphic members:"&RETURN&"¼
  10. + up arrow"&RETURN&"¼
  11. + down arrow"&RETURN&"¼
  12. + dragger"&RETURN&"¼
  13. + backing bar"&RETURN&RETURN&"¼
  14. You may wish to use two additional members, to indicate that the arrow buttons ¼
  15. have been pressed:"&RETURN&"¼
  16. + up arrow (pressed state)"&RETURN&"¼
  17. + down arrow (pressed state)"&RETURN&RETURN&"¼
  18. Place the four standard members on the Stage, and drop this behavior onto each ¼
  19. of them.  Choose how the current sprite is to act in the appropriate pop-up ¼
  20. menu in the Behavior Parameters dialog."&RETURN&RETURN&"¼
  21. For each element you can choose whether animations should continue in the ¼
  22. background.  This option will tend to slow both the animations and the ¼
  23. scrolling process, especially if applied to the arrow buttons."&RETURN&RETURN&"¼
  24. The various sprites will position themselves automatically to the right of the ¼
  25. sprite-to-be-scrolled when the movie runs.  They revert to their original ¼
  26. positions when it stops.  To avoid flashes, it would be a good idea to ¼
  27. position them by hand in their intended positions."&RETURN&RETURN&"¼
  28. If you use a border on your field members, the scrollbar will sit outside the ¼
  29. border.  Field box shadows are not recommended.  You can always fake external ¼
  30. borders and box shadows with shape members (this even gives you a choice of ¼
  31. colors)."&RETURN&RETURN&"¼
  32. To make authoring easier, this behavior will continue to work if you change ¼
  33. EITHER the sprite channel OR the member in the chosen channel.  If you change ¼
  34. both, the behavior will no longer know what to scroll."&RETURN&RETURN&"¼
  35. If you do change either the sprite or the member, and then reopen the Behavior ¼
  36. Parameters dialog for one of the elements, this will put that behavior out of ¼
  37. synch with the others.  Simply reopen and close the Parameters dialogs for ¼
  38. each of the other elements.  If you do not do so, you will receive multiple ¼
  39. alerts."&RETURN&RETURN&"¼
  40. This behavior can be used to scroll both editable and non-editable Fields and ¼
  41. Text members.  For editable members, however, it does not automatically update ¼
  42. the dragger position when the length of the text changes, nor does it make the ¼
  43. editable member scroll automatically when the user drags the mouse to create ¼
  44. a selection."&RETURN&RETURN&"¼
  45. PERMITTED MEMBER TYPES:"&RETURN&PermittedMemberTypes (me)&RETURN&RETURN&"¼
  46. PARAMETERS:"&RETURN&"¼
  47. * Current sprite acts as (up|down arrow | dragger | bar)"&RETURN&"¼
  48. * Scroll the member of <sprite>: <the member of sprite>"&RETURN&"¼
  49. * Standard member (this should not need to be set)"&RETURN&"¼
  50. * Member to display when arrow buttons are pressed"&RETURN&"¼
  51. * Allow animations to continue"&RETURN&RETURN&"¼
  52. PUBLIC METHODS"&RETURN&"¼
  53. => Scroll the text/field member to a given position"&RETURN&"¼
  54. => Swap the text/field  member to be scrolled"&RETURN&"¼
  55. => Get the behavior reference"
  56. end getBehaviorDescription
  57.  
  58.  
  59. on getBehaviorTooltip me
  60.   return " ¼
  61. Use 4 graphic members (dragger, bar, up and down arrows)"&RETURN&"¼
  62. to create a dynamic scrollbar for Text or Field members."&RETURN&RETURN&"¼
  63. Drop this behavior on each sprite separately."&RETURN&RETURN&"¼
  64. Sprites can pass over the scrollbar and animations can"&RETURN&"¼
  65. continue while the user scrolls."&RETURN&RETURN&"¼
  66. The behavior accepts Lingo calls to:"&RETURN&"¼
  67. + set the scrolltop of the scrolled member to a given value."&RETURN&"¼
  68. + swap the current member of the sprite."
  69. end getBehaviorTooltip
  70.  
  71.  
  72.  
  73. -- NOTES FOR DEVELOPERS
  74.  
  75. -- This is the most complex behavior I have written for the Behavior Library.
  76. -- I have included in one script all the handlers necessary to deal with each
  77. -- of the four elements of a scroll bar.
  78. -- 
  79. -- In practice, this one script works as four separate behaviors.  Each element
  80. -- is identified by its myScrollRole (upArrow, downArrow, dragger or bar).  The
  81. -- behavior acts differently for each myScrollRole.  Many handlers are divided
  82. -- into section by a "case myScrollRole of" statement.
  83.  
  84. -- INSTALLATION
  85. -- Initializing the 4 behaviors cannot be done all at once on beginSprite
  86. -- because the behaviors on the other sprites may not exist.  To get round
  87. -- this, I set a myState property to 0 in the StartInstallation handler
  88. -- (on beginSprite).  The first prepareFrame (which is sent once all behaviors
  89. -- in the current frame have been instanciated) sees that "myState + 0 = 0"
  90. -- (or FALSE) and calls the FinishInstallation handler.
  91.  
  92. -- Why "myState + 0"?  Because once the installation is finished, myState is
  93. -- set to #done.  This is a symbol.  A symbol is simply an integer with a
  94. -- special tag.  Adding zero to a symbol gives you access to the integer
  95. -- itself.  Later, when the prepareFrame handler encounters "#done + 0" it
  96. -- evaluates this as a positive integer (or TRUE) and doesn't botherd to
  97. -- reinstall the elements.
  98.  
  99. -- This technique is excessively fast: it slows down the following
  100. -- prepareFrames by something in the order of a millionth of a second.
  101.  
  102. -- Installation itself is a three step-process.
  103.  
  104. -- First: the behavior has to check if the sprite and member that it is to
  105. -- scroll do actually appear where it expects to find them.  The
  106. -- ourScrolledElement property returned by the getPropertyDescriptionList
  107. -- handler is a double-barreled affair: "sprite X:field(member Y of castLib Z)".
  108. -- If sprite X contains a Field or Text member, the behavior assumes that this
  109. -- is the right one, and adopts it as myScrolledMember.  If not, it sets out to
  110. -- look for (member Y of castLib Z), via the FindSprite handler.  If it finds
  111. -- this member in one of the sprites in the frame, then it adopts the new
  112. -- sprite as myScrolledSprite.  If not, it warns the author (4 times, once for
  113. -- each behavior).
  114.  
  115. -- Second: the current behavior has to check if all the others are present.
  116. -- The Initialize handler has already prepared a list of all behaviors which
  117. -- treat the same sprite and/or member: ourControlList.  The CheckControl
  118. -- handler ensures that this list contains one behavior for each control
  119. -- element... and only one.
  120.  
  121. -- Third: the sprite to which the behavior is attached has to be placed
  122. -- correctly beside the sprite it is to scroll.  This is most complex for the
  123. -- Dragger sprite, since it must be placed in accordance with the current
  124. -- scrollTop of myScrolledMember.
  125.  
  126. -- The scrolling itself is always carried out by the behavior attached to the
  127. -- dragger.  The Move and MoveBar handlers (which deal with clicks on the arrow
  128. -- buttons and on the backing bar) end with a...
  129. -- 
  130. --   call (#SetDraggerShift, ourControlList.dragger)
  131. -- 
  132. -- ... command.  The bar behavior must in addition ask the dragger where it now
  133. -- is, using "call (#GetDraggerData, ourControlList.dragger)", so as to update its
  134. -- myActiveZone.
  135.  
  136. -- EXTERNAL LINGO CALLS
  137.  
  138. -- * CustomScrollbar_SetScroll me, theScroll
  139.  
  140. -- If you use standard Lingo to set the scrollTop of myScrolledMember at
  141. -- runtime,  the dragger position will not update until the scroll bar is next
  142. -- used.  Use this call to tell the dragger to do all the work for you
  143.  
  144. -- * CustomScrollbar_SwapMember me, newMember, currentMemberOrSprite
  145.  
  146. -- If you use standard Lingo to swap the member of myScrolledSprite at runtime,
  147. -- the behaviors will continually happily to scroll the member which is now
  148. -- off-stage.  Use this call to tell one of the behaviors do the swapping for
  149. -- you.
  150.  
  151. -- * CustomScrollbar_GetReference me, memberOrSprite, controlOrList
  152.  
  153. -- Using sendAllSprites with one of the above messages will ensure that the
  154. -- job gets done... four times, once by each behavior that makes up the
  155. -- scroll bar.  If you call one of the behaviors directly, the command will be
  156. -- executed just once.  But first you must get an object reference to the
  157. -- behavior in question.  The following syntax is the simplest:
  158. -- 
  159. --   scrollRef = sendAllSprites (#CustomScrollbar_GetReference, sprite X)
  160. -- 
  161. -- This will give you a reference to the behavior on the highest sprite which
  162. -- controls the scrolling of sprite X.
  163.  
  164. -- More details on using these external calls are given in the handlers
  165. -- themselves.
  166.  
  167.  
  168.  
  169. -- HISTORY --
  170.  
  171. -- 27  October 1998: written for the D7 Behaviors Palette by James Newton
  172. -- 24 November 1998: vector shapes abandoned as permitted members
  173.  
  174.  
  175.  
  176. -- PROPERTIES --
  177.  
  178. property spriteNum
  179. -- error checking
  180. property getPDLError
  181. -- author-defined parameters
  182. property ourScrolledElement -- "<spriteNum>: <member>" of element to scroll
  183. property myScrollRole       -- sprite behaves as upArrow|downArrow|dragger|bar
  184. property myActiveMember     -- member which appears on mouseDown
  185. property myStandardMember   -- member which appears at all other times
  186. property myMultiThreading   -- updates on prepareFrame | while the mouseDown
  187. -- internal properties
  188. property mySprite
  189. property myMember
  190. property myRect              -- rect to which all members must be set to fit
  191. property myScrolledSprite    -- sprite containing myScrolledMember
  192. property myScrolledMember    -- Text or Field member to scroll
  193. property myActiveZone        -- when mouse is in zone, scrolling continues
  194. property myState             -- #paused if mouse is outside myActiveZone
  195. property myNextUpdate        -- slows scrollByPage if necessary
  196. -- properties specific to the dragger
  197. property myZeroLoc           -- loc of dragger when scrollTop = 0
  198. property myDraggerShift      -- vertical distance dwon from myZeroLoc
  199. property myClickV            -- vertical coordinate where the dragger was clicked
  200. property myInterimShift      -- value of shift while the dragger is being scrolled
  201. property myMaxDraggerShift   -- maximum value of dragger shift
  202. -- property specific to the bar
  203. property myPageScroll        -- direction of scroll: #up or #down
  204. -- properties shared between behaviors
  205. property ourControlList      -- list of behaviors which collaborate on scrollbar
  206. property ourMaxScroll        -- list shared by behaviors: [maximum scrollTop]
  207.  
  208.  
  209.  
  210.  
  211. -- EVENT HANDLERS --
  212.  
  213. on beginSprite me
  214.   StartInstallation me
  215. end beginSprite
  216.  
  217.  
  218. on prepareFrame me
  219.   if not myState + 0 then FinishInstallation me -- First prepareFrame only
  220.   if myMultiThreading then UpdateScroll me
  221. end prepareFrame
  222.  
  223.  
  224. on mouseDown me
  225.   StartScroll me
  226. end mouseDown
  227.  
  228. on mouseUp me
  229.   EndScroll me
  230. end mouseUp
  231.  
  232. on mouseUpOutside me
  233.   EndScroll me
  234. end mouseUpOutside
  235.  
  236.  
  237.  
  238. -- CUSTOM HANDLERS --
  239.  
  240. on StartInstallation me -- sent by beginSprite
  241.   mySprite         = sprite(me.spriteNum)
  242.   myMember         = mySprite.member
  243.   memberType       = myMember.type
  244.   
  245.   -- Error checking
  246.   if voidP (myScrollRole) then
  247.     ErrorAlert (me, #getPDL_Invalid)
  248.   end if
  249.   
  250.   -- The member of the sprite may have changed since getPDL
  251.   myScrolledSprite = value (ourScrolledElement)
  252.   myScrolledMember = myScrolledSprite.member
  253.   
  254.   -- Ensure that the scrolled sprite has not changed or moved
  255.   memberType = myScrolledMember.type
  256.   case memberType of
  257.     #field, #text: -- do nothing
  258.     otherwise
  259.       -- Try to find the expected member in another sprite
  260.       FindSprite (me)
  261.   end case
  262.   -- End of error checking
  263.   
  264.   -- Calculate ideal sprite.rect
  265.   topLeft = point (myScrolledSprite.left, myScrolledSprite.top)
  266.   myRect  = myScrolledSprite.rect - rect (topLeft, topLeft)
  267.   if memberType = #field then
  268.     if myScrolledMember.border then
  269.       myRect[1] = 1
  270.     end if
  271.   end if
  272.   -- Contact other sprites with the same behavior
  273.   ourMaxScroll = []
  274.   set ourControlList = [:]
  275.   sendAllSprites ¼
  276.  #CustomScrollbar_RollCall, ¼
  277.  ourScrolledElement, ¼
  278.  ourControlList, ¼
  279.  ourMaxScroll ¼
  280. )
  281.   myState = 0 -- Check will be run on first prepareFrame
  282. end StartInstallation
  283.  
  284.  
  285. on FindSprite me -- sent by StartInstallation
  286.   -- Finds highest sprite containing the expected member to scroll
  287.   saveDelimiter = the itemDelimiter
  288.   the itemDelimiter = ":"
  289.   memberData = ourScrolledElement.item[2]
  290.   the itemDelimiter = saveDelimiter
  291.   delete memberData.word[1] -- Remove type information
  292.   delete memberData.char[1] -- Remove remaining space
  293.   memberData = value (memberData)
  294.   myScrolledMember = member (memberData)
  295.   
  296.   scrollSprite = the lastChannel
  297.   repeat while scrollSprite
  298.     if sprite(scrollSprite).member = myScrolledMember then
  299.       -- We've found it!
  300.       myScrolledSprite = sprite (scrollSprite)
  301.       exit
  302.     end if
  303.     scrollSprite = scrollSprite - 1
  304.   end repeat
  305.   if not scrollSprite then
  306.     -- The expected member was not found
  307.     ErrorAlert me, #notScrollable, myScrolledSprite.member.type
  308.   end if
  309. end FindSprite
  310.  
  311.  
  312. on FinishInstallation me -- sent by first prepareFrame
  313.   -- Terminates the initialization process once all behaviors are in place
  314.   CheckControlList me
  315.   -- Place this sprite to the right of myScrolledSprite
  316.   InstallElement me
  317. end FinishInstallation
  318.  
  319.  
  320. on CheckControlList me -- sent by FinishInstallation
  321.   -- Checks if ourControlList contains one-and-only-one behavior with each role
  322.   checkList = duplicate (ourControlList)
  323.   roleList  = [#upArrow, #downArrow, #dragger, #bar]
  324.   i = roleList.count()
  325.   repeat while i
  326.     theRole = roleList[i]
  327.     rolePosition = checkList.findPos (theRole)
  328.     if rolePosition then
  329.       roleList.deleteAt(i)
  330.       checkList.deleteAt (rolePosition)
  331.     end if
  332.     i = i - 1
  333.   end repeat
  334.   if checkList.count() then
  335.     ErrorAlert me, #extraControl, checkList.getPropAt (1)   
  336.   end if
  337.   if roleList.count() then
  338.     ErrorAlert me, #missingControls, roleList 
  339.   end if
  340. end CheckControlList
  341.  
  342.  
  343. on InstallElement me
  344.   -- sent by FinishInstallation on first prepareFrame, _SwapMembers
  345.   -- Places sprite next to myScrolledSprite and determines myActiveZone  
  346.   scrollRoof  = myScrolledSprite.top
  347.   scrollFloor = myScrolledSprite.bottom
  348.   scrollLeft  = myScrolledSprite.right
  349.   if myScrolledMember.type = #field then
  350.     scrollLeft = scrollLeft - (myScrolledMember.border <> 0)
  351.   end if
  352.   
  353.   case myScrollRole of
  354.     #upArrow: 
  355.       mySprite.loc = point (scrollLeft, scrollRoof) + myMember.regPoint
  356.       myActiveZone = mySprite.rect
  357.     #downArrow:
  358.       locAdjust    = myMember.regPoint - [0, mySprite.height]
  359.       rectAdjust   = mySprite.rect - rect (mySprite.loc, mySprite.loc) ---òòò
  360.       mySprite.loc = point (scrollLeft, scrollFloor) + locAdjust
  361.       -- myActiveZone = mySprite.rect ùùùòòò
  362.       myActiveZone = rectAdjust + rect (mySprite.loc, mySprite.loc)
  363.     #bar:
  364.       barRoof      = scrollRoof  + call (#SpriteHeight, ourControlList.upArrow)
  365.       barRight     = scrollLeft  + call (#SpriteWidth,  ourControlList.upArrow)
  366.       barFloor     = scrollFloor - call (#SpriteHeight,ourControlList.downArrow)
  367.       mySprite.rect= rect (scrollLeft, barRoof,    barRight, barFloor)
  368.     #dragger:
  369.       case myMember.type of
  370.         #vectorShape:
  371.           regOffset = (myMember.strokeWidth  + 1) / 2
  372.           myMember.regPoint = point (regOffset, regOffset)
  373.         otherwise
  374.           myMember.regPoint = point (0,0)
  375.       end case
  376.       
  377.       upHeight        = call (#SpriteHeight, ourControlList.upArrow)
  378.       upWidth         = call (#SpriteHeight, ourControlList.upArrow)
  379.       downHeight      = call (#SpriteHeight, ourControlList.downArrow)
  380.       
  381.       draggerRoof     = scrollRoof  + upHeight
  382.       myZeroLoc       = point (scrollLeft, draggerRoof)
  383.       barRight        = scrollLeft  + upWidth
  384.       myActiveZone    = rect (scrollLeft, scrollRoof, barRight, scrollFloor)
  385.       myActiveZone    = inflate (myActiveZone, 32, 32)
  386.       
  387.       arrowAdjust     = upHeight + downHeight
  388.       pageHeight      = myScrolledSprite.height
  389.       scrollHeight    = pageHeight - arrowAdjust
  390.       draggerHeight   = mySprite.Height
  391.       
  392.       myMaxDraggerShift = 0 -- pre-emptive measure, in case dragger can't scroll
  393.       if scrollHeight < 0 then
  394.         -- Adjust length of myScrolledMember so that arrow buttons fit
  395.         minRect               = myScrolledMember.rect
  396.         minRect[4]            = arrowAdjust
  397.         myScrolledMember.rect = minRect
  398.         call (#InstallElement, ourControlList.bar)
  399.       end if
  400.       if scrollHeight < draggerHeight then
  401.         mySprite.loc = point (-999, -999)
  402.       else
  403.         -- Dragger can slide: set myMaxDraggerShift to reflect this
  404.         if myScrolledMember.type = #field then
  405.           theMargin     = myScrolledMember.margin
  406.           theMargin     = theMargin - theMargin mod 2
  407.           borderAdjust  = (myScrolledMember.border * 2) + theMargin
  408.         else
  409.           borderAdjust  = 0
  410.         end if
  411.         myMaxDraggerShift = scrollHeight - draggerHeight + borderAdjust
  412.       end if
  413.       
  414.       ourMaxScroll[1] = GetMaxScroll (me)
  415.       SetDraggerShift me
  416.   end case
  417.   myState = #done
  418. end InstallElement
  419.  
  420.  
  421. on GetMaxScroll me
  422.   pageHeight = myScrolledSprite.height
  423.   
  424.   if myScrolledMember.type = #text then
  425.     lastChar   = myScrolledMember.char.count
  426.     textHeight = charPostoloc  (myScrolledMember, lastChar)[2]
  427.   else
  428.     textHeight = myScrolledMember.height
  429.   end if
  430.   
  431.   maxScroll  = textHeight - pageHeight
  432.   if maxScroll < 1 then
  433.     return 0
  434.   else
  435.     return maxScroll
  436.   end if
  437. end GetMaxScroll
  438.  
  439.  
  440.  
  441. -- SCROLLING --
  442.  
  443. on StartScroll me -- sent by mouseDown
  444.   -- StartInstallations properties for scroll and either performs one update
  445.   -- (if myMultiThreading is TRUE) or completes the operation (in repeat loop).
  446.   myState = #active
  447.   ourMaxScroll[1] = GetMaxScroll (me)
  448.   case myScrollRole of
  449.     #dragger:
  450.       -- Determine start of scroll
  451.       myClickV        = the clickLoc[2]
  452.       myInterimShift = 0
  453.     #upArrow, #downArrow:
  454.       -- Swap members
  455.       mySprite.Member = myActiveMember
  456.     #bar:
  457.       -- Determine active zone
  458.       draggerData = call (#GetDraggerData, ourControlList.dragger)
  459.       if the clickLoc[2] < draggerData.top then
  460.         myActiveZone = GetBarZone (me, draggerData, #up)
  461.       else
  462.         myActiveZone = GetBarZone (me, draggerData, #down)
  463.       end if
  464.   end case
  465.   UpdateScroll me
  466.   if myMultiThreading then exit
  467.   
  468.   -- Use a tight repeat loop to scroll as fast as possible
  469.   repeat while the mouseDown
  470.     UpdateScroll me
  471.   end repeat
  472. end StartScroll
  473.  
  474.  
  475. on UpdateScroll me -- sent by prepareFrame, StartScroll, ResumeScroll
  476.   -- Determines whether to pause or to scroll (and how to)
  477.   case myState of
  478.     #active:
  479.       if inside (the mouseLoc, myActiveZone) then
  480.         case myScrollRole of
  481.           #dragger:   MoveDragger me
  482.           #upArrow:   Move me, TRUE
  483.           #downArrow: Move me, FALSE
  484.           #bar:       MoveBar me
  485.         end case
  486.       else
  487.         PauseScroll me
  488.       end if
  489.     #paused:
  490.       ResumeScroll me
  491.   end case
  492. end UpdateScroll
  493.  
  494.  
  495. on GetBarZone me, draggerData, pageScroll
  496.   -- Determines zone in which the mouse makes the text scroll by page
  497.   if not voidP (pageScroll) then
  498.     myPageScroll  = pageScroll
  499.   end if
  500.   barZone       = mySprite.rect
  501.   if myPageScroll = #up then
  502.     barZone [2] = myScrolledSprite.top
  503.     barZone [4] = draggerData.top
  504.   else 
  505.     barZone [2] = draggerData.bottom
  506.     barZone [4] = myScrolledSprite.bottom
  507.   end if
  508.   return barZone
  509. end GetBarZone
  510.  
  511.  
  512. on MoveDragger me
  513.   -- Checks if the dragger has moved, and if so modifies scroll
  514.   newScroll = myDraggerShift + the mouseV - myClickV
  515.   newScroll = max (0, min (newScroll, myMaxDraggerShift))
  516.   if myInterimShift = newScroll then exit
  517.   
  518.   myInterimShift = newScroll      
  519.   SetTextScroll me, myInterimShift
  520. end MoveDragger
  521.  
  522.  
  523. on Move me, up
  524.   -- Scrolls one line up or down
  525.   if the ticks < myNextUpdate then exit
  526.   if voidP (myNextUpdate) then
  527.     -- Allow time for a quick single-line click
  528.     myNextUpdate = the ticks + 10
  529.   else
  530.     -- Don't scroll too fast
  531.     myNextUpdate = the ticks + 1
  532.   end if
  533.   
  534.   if up then
  535.     if not myScrolledMember.scrollTop then exit
  536.     scrollByLine myScrolledMember, -1
  537.   else
  538.     maxScroll = ourMaxScroll[1]
  539.     if myScrolledMember.scrollTop = maxScroll then exit
  540.     scrollByLine myScrolledMember, 1
  541.     if myScrolledMember.scrollTop > maxScroll then
  542.       myScrolledMember.scrollTop = maxScroll
  543.     end if
  544.   end if
  545.   call (#SetDraggerShift, ourControlList.dragger)
  546. end MoveUp
  547.  
  548.  
  549. on MoveBar me, up
  550.  
  551.   -- Scrolls one page up or down, leaving one previous line visible
  552.   if the ticks < myNextUpdate then exit
  553.   myNextUpdate = the ticks + 10 -- So as not to scroll too fast
  554.   if myPageScroll = #up then
  555.     scrollByPage (myScrolledMember, -1)
  556.     if myScrolledMember.scrollTop then
  557.       -- Leave previous top line visible at bottom
  558.       scrollByLine (myScrolledMember,  1)
  559.     end if
  560.   else -- down
  561.     maxScroll = ourMaxScroll[1]
  562.     if myScrolledMember.scrollTop = maxScroll then exit
  563.     scrollByPage (myScrolledMember,  1)
  564.     -- Leave previous bottom line visible at top
  565.     scrollByLine (myScrolledMember, -1)
  566.     
  567.     if myScrolledMember.scrollTop > maxScroll then
  568.       myScrolledMember.scrollTop = maxScroll
  569.     end if
  570.   end if
  571.   call (#SetDraggerShift, ourControlList.dragger)
  572.   -- The area of the bar where the mouse should active has just got smaller
  573.   draggerData  = call (#GetDraggerData, ourControlList.dragger)
  574.   myActiveZone = GetBarZone (me, draggerData)
  575. end MoveBar
  576.  
  577.  
  578. on PauseScroll me
  579.   -- Pauses the behavior when the mouse leaves myActiveZone
  580.   myState = #paused
  581.   case myScrollRole of
  582.     #dragger:
  583.       -- Revert to original scroll
  584.       SetTextScroll me, myDraggerShift
  585.     #upArrow, #downArrow:
  586.       -- Revert to original button image
  587.       mySprite.member = myStandardMember
  588.       if not myMultiThreading then
  589.         updateStage
  590.       end if
  591.   end case
  592. end PauseScroll
  593.  
  594.  
  595. on ResumeScroll me
  596.   -- Reactivates the behavior when the mouse returns to myActiveZone
  597.   myState = #active
  598.   case myScrollRole of
  599.     #upArrow, #downArrow:
  600.       mySprite.member = myActiveMember
  601.   end case
  602.   UpdateScroll me
  603. end ResumeScroll
  604.  
  605.  
  606. on EndScroll me
  607.   -- Tidies up after scroll is over, ready for the next one
  608.   case myScrollRole of
  609.     #dragger:
  610.       if not voidP (myInterimShift) then
  611.         if ourMaxScroll[1] then
  612.           myDraggerShift = myInterimShift
  613.         else
  614.           myDraggerShift = 0
  615.           ShiftDragger me, myDraggerShift
  616.         end if
  617.       end if
  618.       myInterimShift = void
  619.     #upArrow, #downArrow:
  620.       mySprite.member = myStandardMember
  621.       myNextUpdate = void
  622.   end case
  623.   myState = #done
  624. end EndScroll
  625.  
  626.  
  627. on SetTextScroll me, draggerShift
  628.   -- Calculates the scrollTop of the text, then sets it and moves the thum
  629.   if ourMaxScroll[1] then
  630.     textScroll = (draggerShift * ourMaxScroll[1]) / myMaxDraggerShift
  631.     myScrolledMember.scrollTop = textScroll
  632.   else
  633.     -- draggerShift = 0
  634.   end if
  635.   
  636.   ShiftDragger me, draggerShift
  637. end SetTextScroll
  638.  
  639.  
  640. on SetDraggerShift me
  641.   -- Calculates the position of the dragger from myScrolledMember.scrollTop
  642.   maxScroll = ourMaxScroll[1]
  643.   if maxScroll then
  644.     textScroll = myScrolledMember.scrollTop
  645.     myDraggerShift = (textScroll * myMaxDraggerShift) / maxScroll
  646.   else
  647.     myDraggerShift = 0
  648.   end if
  649.   
  650.   ShiftDragger me, myDraggerShift
  651. end SetDraggerShift
  652.  
  653.  
  654. on ShiftDragger me, draggerShift
  655.   if not myMaxDraggerShift then
  656.     -- Don't move dragger if there is no space to move it
  657.     exit
  658.   end if
  659.   
  660.   mySprite.loc = myZeroLoc + [0, draggerShift]
  661.   updateStage
  662. end ShiftDragger
  663.  
  664.  
  665.  
  666. -- PUBLIC METHODS (responses to #sendSprite, #sendAllSprites, #call) --
  667.  
  668. on CustomScrollbar_SetScroll me, theScroll
  669.   -- Allows an editable field to update its scrolltop via the #dragger behavior
  670.   
  671.   -- Error check
  672.   case ilk (theScroll) of
  673.     #integer: -- nothing
  674.     #float:   theScroll = integer (theScroll)
  675.     otherwise
  676.       return #invalidTypeError
  677.   end case
  678.   -- End of error check
  679.   
  680.   theScroll = max (0, min (theScroll, ourMaxScroll[1]))
  681.   myScrolledMember.scrollTop = theScroll
  682.   call (#SetDraggerShift, ourControlList.dragger, theScroll)
  683. end CustomScrollbar_SetScroll
  684.  
  685.  
  686. on CustomScrollbar_SwapMember me, newMember, currentMemberOrSprite
  687.   -- Allows you to change the member of the scrolled sprite at runtime
  688.   -- newMember is a member reference, name or number.  If you have a number of
  689.   -- custom scrollbars in the frame, then currentMemberOrSprite identifies 
  690.   -- which member to change if you use a sendAllSprites messsage.
  691.   -- You can leave the currentMemberOrSprite parameter empty if you call a
  692.   -- specific behavior.
  693.   --
  694.   -- The following examples both set the member of sprite 1 to member 2.
  695.   -- The first example will actually do this four times, once for each
  696.   -- behavior that makes up the scrollbar:
  697.   --
  698.   --   sendAllSprites (#CustomScrollbar_SwapMember, member 2, sprite 1)
  699.   -- OR
  700.   --   scrollBehavior = sendAllSprites (#CustomScrollbar_GetReference, sprite 1)
  701.   --   call (#CustomScrollbar_SwapMember, scrollBehavior, member 2)
  702.   
  703.   -- Error check
  704.   case ilk (newMember) of
  705.     #member: -- do nothing
  706.     #integer, #string:   
  707.       memberExists = (the number of member (newMember) > 0)
  708.       if memberExists then
  709.         newMember = member (newMember)
  710.       else
  711.         return #memberInexistant
  712.       end if
  713.     otherwise
  714.       return #invalidType
  715.   end case
  716.   case newMember.type of
  717.     #text, #field: -- do nothing
  718.     otherwise
  719.       return #invalidMemberType
  720.   end case
  721.   -- End of error check
  722.   
  723.   case ilk (currentMemberOrSprite) of
  724.     #sprite: if currentMemberOrSprite <> myScrolledSprite then exit  
  725.     #member: if currentMemberOrSprite <> myScrolledMember then exit
  726.       -- otherwise ignore currentMemberOrSprite
  727.   end case
  728.   
  729.   myScrolledMember = newMember
  730.   idealRect        = myRect
  731.   if myScrolledMember.type = #field then
  732.     hBorder        = myScrolledMember.border
  733.     vBorder        = hBorder - (hBorder <> 0)
  734.     hMargin        = myScrolledMember.margin
  735.     vMargin        = hMargin / 2   --- hMargin - (hMargin mod 2)
  736.     idealRect      =inflate (idealRect, -hBorder-hMargin, -hBorder-vMargin)
  737.     if myRect[1] then -- 0riginal meber had a border...
  738.       if hBorder then -- ... and so does this member
  739.         idealRect[1] = idealRect[1] - 1
  740.       end if
  741.     else -- Original member had NO border...
  742.       if hBorder then -- ... but this one has.
  743.         idealRect[1] = idealRect[1] - 1
  744.       end if
  745.     end if
  746.   end if
  747.   
  748.   myScrolledMember.rect   = idealRect
  749.   myScrolledSprite.member = myScrolledMember
  750.   myScrolledMember.boxType = #fixed
  751.   
  752.   i = ourControlList.count()
  753.   repeat while i
  754.     call (#NewMember, ourControlList[i])
  755.     i = i - 1
  756.   end repeat
  757.   updateStage
  758.   call (#InstallElement, ourControlList.dragger)
  759. end CustomScrollbar_SwapMember
  760.  
  761.  
  762. on CustomScrollbar_GetReference me, memberOrSprite, controlOrList
  763.   -- Returns a reference to the behavior for Lingo calls.
  764.   -- The parameters are optional: use them to find a specific behavior.
  765.   -- Use 'theElement' to find the scrollbar of a particular sprite or member
  766.   -- Use 'controlOrList' to find a specific behavior (up|downArrow|dragger|bar)
  767.   -- or to return a property list of all behaviors.
  768.   -- Examples:
  769.   --
  770.   -- put sendAllSprites (#CustomScrollbar_GetReference, sprite 1, #dragger)
  771.   -- -- <offspring "Custom Scrollbar" 3 486bc20>
  772.   --
  773.   -- put sendAllSprites (#CustomScrollbar_GetReference, sprite 1, [:])
  774.   -- -- #bar: <ref>, #upArrow: <ref>, #dragger: <ref>, #downArrow: <ref>]
  775.   --
  776.   -- You can leave the controlOrList parameter empty.  If you do, the
  777.   -- behavior reference on the highest sprite in the scrollbar will be returned.
  778.   
  779.   case ilk (memberOrSprite) of
  780.     #sprite: if memberOrSprite <> myScrolledSprite then exit  
  781.     #member: if memberOrSprite <> myScrolledMember then exit
  782.     otherwise
  783.       exit
  784.   end case
  785.   
  786.   if not voidP (controlOrList) then
  787.     if ilk (controlOrList) = #propList then
  788.       controlOrList.addProp(myScrollRole, me)
  789.       return controlOrList
  790.     else
  791.       if controlOrList = myScrollRole then return me
  792.     end if
  793.   else
  794.     return me
  795.   end if
  796. end CustomScrollbar_GetReference
  797.  
  798.  
  799.  
  800. -- INTER-SPRITE COMMUNICATION --
  801. -- (responses to #sendAllSprites) --
  802.  
  803. on CustomScrollbar_RollCall me, scrolledElement, controlList, maxScrollList
  804.   -- sent on StartInstallation by this and other sprites with the same behavior
  805.   if scrolledElement <> ourScrolledElement then exit
  806.   
  807.   ourControlList = controlList
  808.   ourControlList.addProp(myScrollRole, me)
  809.   ourMaxScroll   = maxScrollList
  810. end CustomScrollbar_RollCall
  811.  
  812. -- (responses to #call) --
  813.  
  814. on SpriteHeight me
  815.   -- Called by InstallElement in behaviors on the bar and the dragger
  816.   -- Dealt with by behavior on the upArrow
  817.   return mySprite.height
  818. end
  819.  
  820.  
  821. on SpriteWidth me
  822.   -- Called by InstallElement in behaviors on the bar and the dragger
  823.   -- Dealt with by behavior on the upArrow
  824.   return mySprite.width
  825. end
  826.  
  827.  
  828. on GetDraggerData me
  829.   -- Called by  StartScroll, MoveBar in behavior on the bar
  830.   -- Dealt with by behavior on the dragger
  831.   return [#top: mySprite.top, #bottom: mySprite.bottom]
  832. end
  833.  
  834.  
  835. on NewMember me -- sent by _SwapMember from the behavior that received the call
  836.   myScrolledMember = myScrolledSprite.member
  837. end NewMember
  838.  
  839.  
  840.  
  841. -- ERROR CHECKING --
  842.  
  843. on ErrorAlert me, theError, data
  844.   -- sent by getPropertyDescriptionList, StartInstallation
  845.   case theError of
  846.     #getPDLError:
  847.       permittedTypes = PermittedMemberTypes(me)
  848.       alert "¼
  849. Error: This behavior works only with the following member types:  "&¼
  850. permittedTypes&RETURN&RETURN&"¼
  851. Hit OK and then delete this behavior from the sprite."&RETURN&"¼
  852. For more information on deleting Behaviors, see the Help system."
  853.       if the optionDown then
  854.         return ¼
  855. [ ¼
  856.  #getPDLError: ¼
  857.  [ ¼
  858.   #comment: "ERROR:  Click 'Cancel'. Wrong member type.", ¼
  859.   #format:  #string, ¼
  860.   #range:   [""], ¼
  861.   #default:  "" ¼
  862.  ] ¼
  863. ]
  864.       end if
  865.       
  866.     #noScrollableSprites:
  867.       alert "¼
  868. Error: Please place a Field or Text member on the Stage before using this ¼
  869. behavior to create a scroll bar."&RETURN&RETURN&"¼
  870. Hit OK and then delete this behavior from the sprite."&RETURN&"¼
  871. For more information on deleting Behaviors, see the Help system."
  872.       if the optionDown then
  873.         return ¼
  874. [ ¼
  875.  #getPDLError: ¼
  876.  [ ¼
  877.   #comment: "ERROR:   Click 'Cancel'.  Place a Text or Field"&RETURN&¼
  878.             "member on Stage before creating scroll bar.", ¼
  879.   #format:  #string, ¼
  880.   #range:   [""], ¼
  881.   #default:  "" ¼
  882.  ] ¼
  883. ]
  884.       end if
  885.       
  886.     otherwise
  887.       -- Determine the behavior's name
  888.       behaviorName = string (me)
  889.       delete word 1 of behaviorName
  890.       delete the last word of behaviorName
  891.       delete the last word of behaviorName
  892.       -- Convert #data to useful value
  893.       case data.ilk of
  894.         #void: data = "<void>"
  895.         #symbol: data = "#"&data
  896.       end case
  897.       if theError <> #getPDL_Invalid then
  898.         -- Determine the name and type of myScrolledMember
  899.         memberName = myScrolledMember.name
  900.         if memberName = EMPTY then
  901.           memberName = myScrolledMember
  902.         else
  903.           memberName = QUOTE&memberName"E
  904.         end if
  905.         memberName = myScrolledMember.type&&memberName
  906.       end if
  907.       
  908.       case theError of
  909.         #getPDL_Invalid:
  910.           alert "¼
  911. BEHAVIOR ERROR: Frame "&the frame&", Sprite "&me.spriteNum&RETURN&RETURN&"¼
  912. Parameters for the "&behaviorName&"behavior have not been set."&RETURN&RETURN&"¼
  913. Please reopen the Behavior Parameters dialog and choose again."
  914.           halt
  915.           
  916.         #notScrollable:
  917.           alert "¼
  918. BEHAVIOR ERROR: Frame "&the frame&", Sprite "&me.spriteNum&RETURN&"¼
  919. Behavior "&behaviorName&RETURN&RETURN&"¼
  920. Sprite "&myScrolledSprite.spriteNum&" does not ¼
  921. contain a Field or Text member."&RETURN&"¼
  922. Choose again in the Behavior Parameters dialog."&¼
  923. RETURN&RETURN&"¼
  924. Member type = "&data
  925.           halt
  926.           
  927.         #extraControl:
  928.           if the runMode = "Author" then
  929.             alert "¼
  930. BEHAVIOR ERROR: Frame "&the frame&", Sprite "&me.spriteNum&RETURN&"¼
  931. Behavior "&behaviorName&RETURN&RETURN&"¼
  932. There is more than one "&data&" sprite defined for the scroll bar for ¼
  933. sprite "&myScrolledSprite.spriteNum&", "&memberName&"."
  934.           end if
  935.           
  936.         #missingControls:
  937.           alert "¼
  938. BEHAVIOR ERROR: Frame "&the frame&", Sprite "&me.spriteNum&RETURN&"¼
  939. Behavior "&behaviorName&RETURN&RETURN&"¼
  940. The following elements of the scroll bar for sprite "&¼
  941. myScrolledSprite.spriteNum&", "&memberName&" are missing:"&RETURN&RETURN&¼
  942. data
  943.           halt
  944.           
  945.       end case
  946.   end case
  947. end ErrorAlert
  948.  
  949.  
  950.  
  951. -- AUTHOR-DEFINED PARAMETERS --
  952.  
  953. on getPropertyDescriptionList me
  954.   
  955.   if not the currentSpriteNum then exit
  956.   
  957.   -- Error check: does current sprite contain appropriate member type?
  958.   theMember = sprite(the currentSpriteNum).member
  959.   memberType = theMember.type
  960.   permittedTypes = PermittedMemberTypes(me)
  961.   if not permittedTypes.getPos(memberType) then
  962.     return ErrorAlert (me, #getPDLError, permittedTypes)
  963.   end if
  964.   -- Find sprites with field or text members
  965.   scrollableList = GetScrollableSprites (me)
  966.   if not scrollableList.count then
  967.     return ErrorAlert (me, #noScrollableSprites)
  968.   end if
  969.   
  970.   return ¼
  971. [ ¼
  972.  #myScrollRole: ¼
  973.  [ ¼
  974.   #comment: "Current sprite acts as:", ¼
  975.   #format:  #symbol, ¼
  976.   #range:  [#upArrow, #downArrow, #dragger, #bar], ¼
  977.   #default:  1 ¼
  978.  ], ¼
  979.  #ourScrolledElement: ¼
  980.  [ ¼
  981.   #comment: "Scroll the member of", ¼
  982.   #format:  #string, ¼
  983.   #range:    scrollableList, ¼
  984.   #default:  scrollableList[1] ¼
  985.  ], ¼
  986.  #myStandardMember: ¼
  987.  [ ¼
  988.   #comment: "Standard member:", ¼
  989.   #format:  #graphic, ¼
  990.   #default:  theMember ¼
  991.  ], ¼
  992.  #myActiveMember: ¼
  993.  [ ¼
  994.   #comment: "(Arrows only) mouseDown member:", ¼
  995.   #format:  #graphic, ¼
  996.   #default:  theMember ¼
  997.  ], ¼
  998.  #myMultiThreading: ¼
  999.  [ ¼
  1000.   #comment: "Allow animations to continue (slower)?", ¼
  1001.   #format:  #boolean, ¼
  1002.   #default:  TRUE ¼
  1003.  ] ¼
  1004. ]
  1005. end getPropertyDescriptionList
  1006.  
  1007.  
  1008. on GetScrollableSprites me, permittedTypes
  1009.   -- Returns a list of sprites containing Field or Text members, in the format:
  1010.   -- ["<spriteNumber>, <type> <member.name|member>",...]
  1011.   
  1012.   scrollableSprites = []
  1013.   repeat with theSprite = 1 to the lastChannel
  1014.     theMember = sprite(theSprite).member
  1015.     case theMember.type of
  1016.       #field, #text:
  1017.         memberName = theMember.name
  1018.         if memberName = EMPTY then
  1019.           memberName = theMember
  1020.         else
  1021.           memberName = QUOTE&memberName"E
  1022.         end if
  1023.         memberName = theMember.type&&memberName
  1024.         scrollableSprites.append ("sprite "&theSprite&": "&memberName)
  1025.     end case
  1026.   end repeat
  1027.   return scrollableSprites
  1028. end GetScrollableSprites
  1029.  
  1030.  
  1031. on PermittedMemberTypes me
  1032.   -- sent by:
  1033.   -- getBehaviorDescription
  1034.   -- getPropertyDescriptionList
  1035.   -- ErrorAlert
  1036.   return [#animgif, #bitmap, #filmLoop, #flash, #movie, #picture, #shape]
  1037. end PermittedMemberTypes
  1038.